# Python的设计哲学：一个问题只提供一种解决方案

# 备注
# PEP8是 Python里一套编程规范（即软性要求）
# Python中只有字符串类型，没有字符类型！'a'看作长度为 1的字符串
# 正是因为 Python没有字符类型，所以'和"都能用来表示字符串
# Python里的 None类似于空指针，表示什么都没有

# 快捷键
# 注释快捷键 Ctrl + / (\是反斜杠)
# 撤回快捷键 Ctrl + z

# # 格式化输出
# f-string 此处 f表示 format,和{}一起用(大括号里的是变量)
# a=10
# print(f"a=+{a}")
# print(f"a=+{a+10}")

# # 输入函数
# num = input('请输入一个数字：') # input返回值为字符串类型
# print(f'您输入的数字是：{num}')

# # 类型转换
# a = input('请输入第一个整数:')
# b = input('请输入第二个整数:')
# a = int(a) #转换数据类型：str -> int
# b = int(b)
# # print(f'a + b = {a + b}')

# # 算术运算符
# # **表示乘方运算，既能乘方也能开方
# print(4 ** 2)
# print(4 ** 0.5)
# # //表示地板除法(取整除法)，向下取整
# print(7 // 2)
# print(-7 // 2)

# # 关系运算符
# a = 10
# b = 20
# # 字符串按字典序比较先后顺序，字符串在词典中越靠前则越小
# #  中文进行字符串比较无意义
# a = 'hello'
# b = 'world'
# print(a > b)
# print(a < b)
# print(a >= b)
# print(a >= b)
# print(a == b)
# print(a != b)
# # 浮点数用==比较相等会误判
# print(0.1 + 0.2 == 0.3) #返回 False
# # 正确比较浮点数是否相等的方法是作差，看差值是否小于预期的误差范围(站在工程的角度误差极小视为相等)
# a = 0.1
# b = 0.2
# print(- 0.000001 < (a - b) < 0.000001) #Python支持连续小于的写法

# # 逻辑运算符
# a = 10
# b = 20
# c = 30
# print(a < b < c) # a < b < c 等价于 a < b and b < c
# print(a < b and b > c) # 一假则假
# print(a < b or b < c) # 一真则真
# print(a < b or b > c)
# print(a > b or b > c)
# print(not a < b) # 逻辑取反
# print(not a > b)
# #短路求值
# print(a > b and 1 + 1) # 对于 and 操作符，左侧为假，整体为假，右侧忽略
# print(a < b or 1 + 1) # 对于 or 操作符，左侧为真，整体为真，右侧忽略

# # 赋值运算符
# # 链式赋值 (不建议使用，因为一行代码一般只执行一个操作)
# a = b = 10  # 表示先把 10赋给b,再把b的值赋给a
# # 多元赋值 (可以用于数值交换)
# a, b = 10, 20
# a, b = b, a
# tmp = a # 数值交换的一般写法
# a = b
# b = tmp
# -=, +=, *=, /=, **=, //=
# a=10
# ++a #a前的+视为正号，-视为负号。Python不支持a++,a--这样的自增自减操作

# 条件语句(也叫分支语句)
# choice = input("输入数字：")
# if choice == '1':
#     print('yes')
# elif choice == '2':
#     print('No')
# else:
#     print('输入有误')

# # 缩进(Python中缩进是硬性要求)
# a = input("输入第一个数字")
# b = input("输入第二个数字")
# if a == '1':
#     if b == '2':
#         print("aaa")
#     print("bbb")
# print("ccc")
#
# a = int(input('输入一个数字：'))
# if a % 2 == 1:
#     print('奇数') #在Python中负奇数%2结果是1
# else:
#     print('偶数')

# year = int(input('输入一个年份：'))
# if year % 4 == 0:
#     if year % 100 == 0:
#         print('世纪闰年')
#     else:
#         print("闰年")
# else:
#     print("平年")

# # 空语句
# a = input("输入一个数字")
# if a != "1":
#     pass # 空语句pass用来占位，以满足格式需要
# else:
#     print("hello")

# #循环语句
# while循环
# Sum = 0
# num = 1
# while num <= 5:
#     a = 1
#     i = 1
#     while i <= num: # 循环嵌套
#         a *= i
#         i += 1
#     Sum += a
#     num += 1
# print(f"sum = {Sum}")

# # for循环
# # range 是一个内建函数，效果是得到一个可迭代对象，range(a,b) -> [a,b)
# for i in range(1, 11, 2): # range的第三个参数叫步长
#     print(i)
# for j in range(10, 0, -1):
#     print(j)

# continue 和 break
# for i in range(1,6):
#     if i == 3:
#         continue # 中断此次循环，进行下次循环
#     print(i)
# for i in range(1, 6):
#     if i == 3:
#         break # 终止整个循环
#     print(i)
# Sum = 0
# n = 0
# while True:
#     num = input("输入一个数字：")
#     if num == '.':
#         break # 当输入'.'时，结束输入
#     num = float(num)
#     Sum += num
#     n += 1
# print(f"平均值为：{Sum / n}")

# # 函数(可以看作一段可以重复使用的代码)
# def calc_sum(beg, end): # 定义一个求和函数
#     Sum = 0
#     for i in range(beg, end + 1):
#         Sum += i
#     print(f"求和结果为：{Sum}")
# calc_sum(1, 100) # 调用函数

# # 函数的语法格式
"""1.函数的定义（分配任务）
  def 函数名（形参列表）： #形参即形式参数，（）里也可以没有形参
    函数体
    return 返回值 #返回值是函数的运算结果，return语句不是必须有
2.函数的调用（执行任务）
  (1)函数名（实参列表） #实参即实际参数
  (2)返回值 = 函数名（实参列表）
注：函数不调用就不会执行
   定义要在调用之前
"""

# def Add(a, b, c):
#     print(a, b, c)
# Add(1, 'Hello', True) # Python中形参和实参个数要求一样，类型无硬性要求
# def add(x, y):
#     return x + y
# 将运算和打印分开其实是让 逻辑 与 交互/界面 分离，解耦合
# a = add(10, 20)
# print(a)
# print(add('hello','world'))

# #一般多个 return 语句是搭配分支语句/循环语句的
# def isOdd(num):
#     if num % 2 == 0:
#         return False
#     else:
#         return True
# def isOdd(num):
#     if num % 2 == 0:
#         return False
#     return True # return 了 False 就不会 return True
# Python中一个函数能返回多个值

# def getPoint():
#     x = 10
#     y = 20
#     z = 30
#     return x, y, z
# a, b , c = getPoint()
# print((a, b ,c))
# _, _, d = getPoint() # _用来占位

#变量作用域
# x = 10 #全局变量
# def test():
#     x = 20 #局部变量
#     print(f"函数内部：{x}")
# test()
# print(f"函数外部：{x}")
# y =1
# def f():
#     print(y) #函数在内部读取不到局不变量，则会读取全局变量
# f()
# z = 1
# def g(): #此函数可将全局变量变成 2
#     global z  # global是声明全局变量的关键字
#     z = 2
# g()
# print(z)

# 函数的链式调用（将一个函数的返回值作为另一个函数的参数）
# def isOdd(num):
#     if num % 2 == 0:
#         return False
#     return True
# def add(x, y):
#     return x + y
# print(isOdd(add(5, 5)))

# 函数的嵌套调用（在一个函数内部调用另一个函数）
# def a():
#     print("a")
# def b():
#     print("b")
#     a()
# def c():
#     print("c")
#     b()
# c()
# def a():
#     print("a")
# def b():
#     a()
#     print("b")
# def c():
#     b()
#     print("c")
# c()

# 函数递归（调用自己）
# 递归代码容易出现“栈（函数的调用栈）溢出”的情况（无法达到递归结束的条件）
# def factor(n):
#     result = 1
#     for i in range(1, n + 1):
#         result *= i
#     return result
#
# print(factor(5))
# def f(n):
#     result = 1
#     if n == 1:
#         return 1
#     return n * f(n - 1)
# print(f(5))

# # 带默认值的形参（可以有多个）
# def add(x, y, debug=False): # 带默认值的形参必须写在后面
#     if debug:
#         print(f"x = {x}, y = {y}")
#     return x + y
# print(add(1, 2)) # 带有默认值的形参不必传参
# print(add(3, 4, True))
# print(add(debug=True,y =5, x = 6))  #关键字传参

# # 列表
# print(list('hello'))
# a = [1, 2, 3, 4]
# b = [1, 'hello', True, [1,2,3]]
# print(a[2])
# a[3] = 100
# print(a)
# print(a[-1]) # a[len(a)-1] 的简便写法（负号可以理解为倒着数）
# 切片（获得一个子列表）# 切片操作不涉及数据拷贝，很高效
# a = [0, 1, 2, 3, 4]
# print(a[1:4]) # 取[1,4)
# print(a[1:]) # 省略后边界
# print(a[:4]) # 省略前边界
# print(a[:]) # 打印本身
# print(a[:-1])
#print(1, 100) # 切片操作范围允许超出列表长度
# a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
# print(a[::2]) # 第二个:后是步长
# print(a[a[::-1]]) # 步长为负数表示倒着取
# # 遍历列表元素
# a = [1, 2, 3, 4, 5]
# for elem in a:
#     print(elem)
#     elem += 10 # 不改变列表
# print(a)
# for i in range(0, len(a)):
#     print(a[i]) # 刚好取a[0]到a[len(a)-1],即全部元素
#     a[i] += 10 # 改变了列表
# print(a)
# i =0
# while i < len(a):
#     print(a[i])
#     i += 1
# 增删元素
# append()，insert()等要搭配对象（变量）使用的函数（function）叫作‘方法’（method）
# a = [1, 2, 3, 4, 5]
# a.append(6) 加在后面
# a.append('hello')
# a.insert(1, 'hello') # 这表示在a[1]前插入' hello'
# a.insert(100, 'hello')
# print(a)
# a.pop() # 删后面的一个元素
# print(a)
# a.pop(1) # 按照下标删除
# print(a)
# a.remove(4) #直接删除具体元素
# print(a)
# 判断元素存在及查找
# a = [1, 2, 3, 4, 5]
# print(1 in a) # in用来判断元素是否在列表中，返回布尔值
# print(2 not in a) # not对in逻辑取反
# print(a.index(5)) #返回下标,可用于查找列表中的元素
# 拼接列表
# a = [1, 2, 3, 4]
# b = [5, 6, 7, 8]
# c = a + b
# print(c)
# a.extend(b) # a改变，b不变
# # 函数extend无返回值，c = a.extend(b)的运行结果为None
# print(a)
# a += b # 涉及数据的构造与释放，比extend更低效
# print(a)
# print(a[1:1])
# a[1:1] = b
# print(a)

# 元组
# 列表中的元素可以修改，元组不可以（即只可以只读）
# print((1, 'hello',True, []))
# a = (1, 2, 3, 4, 5) # tuple()也能表示元组
# print(a[1], a[-1])
# print(a[1:3])
# 多元赋值是按照元组的方式工作的
# def getPoint():
#     x = 1
#     y = 2
#     return x, y
# a, b = getPoint()
# print(type(getPoint()))

# 字典（用来储存键值对的数据类型）
# 键值对是键(key)与值(value)的映射关系
# 字典对key的类型有约束，对value的类型无约束
# 对字典的操作都是针对key进行的
# key不能重复
'''
对于字典来说，使用in或者[]来获取value都很高效（字典的底层是哈希表）
对于列表来说，使用in比较低效（需要遍历列表），使用[]比较高效
'''
# print(type(dict()))
# a = {'ID': 1,
#     "name": 'Mike'
# }
# print(a)
# print("ID" in a) # in只用来判定key是否存在，与value无关
# print("Mike" in a) # 返回False
# print(a['ID'])
# 增删元素
# a['score'] = 90 # 新增
# print(a)
# a['score'] = 100 # 修改
# print(a)
# a.pop("name")
# print(a)
# 遍历
# b = {'ID': 1,
#     "name": 'Mike',
#     'score': 100
# }
# for key in b:
#     print(key, b[key])
# print(b.keys()) # 返回的结果是自定义类型，可以看作列表
# print(b.values())
# print(b.items())
# for key, value in b.items():
#     print(key,value)
# print(hash(0))
# print(hash(3.14))
# print(hash("hello"))
# print(hash(True))
# print(hash((1, 2, 3)))
# 列表和字典不可哈希（即不可以计算哈希值）
# 不可变的对象一般可哈希，可变的对象一般不可哈希



# 文件操作
# f = open('d:/_Python/Note/test1.txt', 'r')
# print(f)
# print(type(f))
# # f此处表示file的缩写，f是文件对象（是内存的一个变量），用于间接操作硬盘内容
# # r表示read,按照读方式打开
# # w表示write,按照直接写方式打开
# # a表示append,按照追加写方式打开，把内容写到原有文件内容的末尾
# f.close()

# 文件打开个数有上限
# file_list = []
# count = 0
# while True:
#     f = open('d:/_Python/Note/test1.txt', 'r')
#     file_list.append(f)
#     count += 1
#     print(f'打开文件的个数:{count}')

# 写文件
# f = open('d:/_Python/Note/test1.txt', 'w')
# f.write('hello')
# f.close()
#
# f = open('d:/_Python/Note/test1.txt', 'a')
# f.write(' world')
# f.close()

# 读文件
# f = open('d:/_Python/Note/test2.txt', 'r',encoding='utf8')
# result = f.read(2) # 读取两个字符
# print(result)
# f.close()
#
# f = open('d:/_Python/Note/test2.txt', 'r', encoding='utf8')
# serial_number = 1
# for line in f: # 用for循环读取文件时是按行的，文件每一行末尾自带换行符
#     print(f'line{serial_number}: {line}', end='')
#     serial_number += 1
# f.close()
#
# f = open('d:/_Python/Note/test2.txt', 'r', encoding='utf8')
# lines = f.readlines() # readlines是按行读取的方法
# print(lines)
# f.close()
# 文件资源管理器
# 类似C++中的智能指针，Java中的try  with Resources，Golang中的defer
# def func():
#     with open('d:/_Python/Note/test2.txt') as f:
#     # with as 能确保文件在使用完毕后自动关闭，避免了因忘记调用 close() 方法而导致的资源泄漏。
#         lines = f.read()
#         print(lines)



# 标准库的使用
# 实例1：日期计算器
# import datetime
# # 先构造datetime变量
# date1 = datetime.datetime(year=2012, month=2, day=14)
# date2 = datetime.datetime(year=2016, month=2, day=3)
# print(date2 - date1)
#
# from datetime import datetime
# date1 = datetime(year=2012, month=2, day=14)
# date2 = datetime(year=2016, month=2, day=3)
# print(date2 - date1)
#
# import datetime as dt
# date1 = dt.datetime(year=2012, month=2, day=14)
# date2 = dt.datetime(year=2016, month=2, day=3)
# print(date2 - date1)



# 实例2：文件查找工具
# import os # operating system 操作系统
#
# path = input('请输入要搜索的路径：')
# file = input('请输入要搜索的关键词：')
#
# # os.walk效果演示
# # for dir_path, dir_names, filenames in os.walk(path):    # directory 目录
# #     print('----------------------------------')
# #     print(f'dir_path:{dir_path}')
# #     for dir_name in dir_names:
# #         print(dir_name)
# #     for filename in filenames:
# #         print(filename)
#
# for dir_path, _, filenames in os.walk(path):
#     for f in filenames:
#         if file in f:
#             print(f'{dir_path}/{f}')